跳到主要内容

Go 依赖注入工具 wire

Wire 是一个的 Go 依赖注入工具,通过自动生成代码的方式在编译期完成依赖注入

go get github.com/google/wire/cmd/wire

都知道要依赖反转,如下这种方式

func initApp() *App {
c := GetRedisConf()
r := NewRedis(c)
app := NewApp(r)
return app
}

func main() {
app := initApp()
app.Run()
}

但是上面那种手工依赖注入也存在一些问题

目前传入的参数都基本只有一个,这样手写注入过程还可以,一旦你要维护的东西多了,比如你的 NewApp 是这样的

func NewApp(r *Redis, es *ES, us *UserSerivce, db *MySQL) *App

然后其中 UserService 是这样的

func UserService(pg *Postgres, mm *Memcached)

这样形成了多层次的一堆依赖需要注入,徒手去写非常麻烦。

而这部分,就是 wire 这样的依赖注入工具能够起作用的地方了——他的功能只是通过生成代码帮你注入依赖,而 实际的依赖实例需要你自己创建(初始化)

wire 中的两个概念:

  • Provider:负责创建对象的方法(就是类似于 Spring 中的 Bean)
  • Injector:负责根据对象的依赖,依次构造依赖对象,最终构造目的对象的方法

示例代码:

首先要实现一个 wire.go 的文件,里面分别实现好 Provider。

func GetRedisConf() *RedisConfig {
// ...
}

func NewRedis(conf *RedisConfig) DataSource {
// ...
}

func SomeProviderSet() *SomeProvider {
// ...
}

func NewApp(ds DataSource) *App {
return &App{ds: ds}
}

然后定义好 Injector。

// +build wireinject

func initApp() (*App) {
panic(wire.Build(GetRedisConf, NewRedis, SomeProviderSet, NewApp))
}

执行 wire 命令后 他会扫描整个项目,并帮你生成一个 wire_gen.go 文件,如果你有什么没有实现好,它会报错出来。

提示

执行 wire 命令生成代码,工具会扫描你的代码,依照你的 Injector 定义来组织各个 Provider 的执行顺序,并自动按照 Provider 们的类型需求来按照顺序执行和安排参数传递,如果有哪些 Provider 的要求没有满足,会在终端报出来,持续修复执行 wire,直到成功生成 wire_gen.go 文件。

它生成的代码其实就是类似我们之前需要手写的这个

func initApp() *App {  // injector
c := GetRedisConf() // provider
r := NewRedis(c) // provider
app := NewApp(r) // provider
return app
}

打包注入

wire 里面还有个 ProviderSet 的概念,就是把一组 Provider 打包

举例:

// +build wireinject

var dbLogSet = wire.NewSet(Logger, dbProvider)

// 然后实现各个方法
func Logger(ctx context.Context) log {
// ...
}

func dbProvider(ctx context.Context) *gorm.DB {
// ...
}

func NewTestService(ctx context.Context) service.TestService {
// 这里引用了上面定义的 dbLogSet
wire.Build(service.NewTestService, dbLogSet, redisCacheProvider)
return nil
}

生成的代码:

// 省略...

func NewTestService(ctx context.Context) service.TestService {
db := dbProvider(ctx)
wrapYKLogger := Logger(ctx)
redisUtil := redisCacheProvider(ctx)
testService := service.NewTestService(ctx, wrapYKLogger, redisUtil)
return testService
}

References